/*
 * Created on Feb 15, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell.ui.command;

import it.okkam.longwell.ui.command.ChangeModelCommand;
import it.okkam.longwell.ui.command.SelectGraphsCommand;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.velocity.VelocityContext;


import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.impl.PropertyImpl;
import com.hp.hpl.jena.tdb.TDBFactory;
import com.hp.hpl.jena.vocabulary.DC;
import com.hp.hpl.jena.vocabulary.RDF;

import edu.mit.simile.longwell.LongwellServer;
import edu.mit.simile.longwell.LongwellServlet;
import edu.mit.simile.longwell.Profile;

import edu.mit.simile.longwell.query.IAnswer;
import edu.mit.simile.longwell.query.IQueryModel;
import edu.mit.simile.longwell.query.Query;
import edu.mit.simile.longwell.query.QueryException;
import edu.mit.simile.longwell.schema.IStructuredProperty;
import edu.mit.simile.longwell.schema.IStructuredSchema;
import edu.mit.simile.longwell.schema.impl.StructuredSchema;
import edu.mit.simile.longwell.tag.ITagModel;
import edu.mit.simile.longwell.ui.FlairMessage;
import edu.mit.simile.longwell.ui.FlairURL;
import edu.mit.simile.longwell.ui.InjectionManager;


/**
 * @author dfhuynh
 */
public class BrowseCommand extends CommandBase {
	final static private Logger s_logger = Logger.getLogger(BrowseCommand.class);
	static private HashMap<String, Dataset> m_graphsByDataset;
	private SortedSet sGraphs;
	InjectionManager inje;
	/**
	 * @param injectionManager
	 */
	public BrowseCommand(InjectionManager injectionManager) {
		super(injectionManager);
		inje=injectionManager;
	}

	/* (non-Javadoc)
	 * @see edu.mit.simile.flair.IFlairCommand#execute(edu.mit.simile.flair.FlairMessage)
	 */
	public void execute(FlairMessage msg) throws ServletException {
		try {
			VelocityContext vcContext = createContext(msg);
			HttpSession session = msg.m_request.getSession();
			String sessionId = msg.m_request.getSession().getId();
			vcContext.put("sessionId", msg.m_request.getSession().getId());
			vcContext.put("graphs", getGraphsFromDefaultModel(msg,(String[]) session.getAttribute(sessionId)));
			vcContext.put("graphsSelected", getGraphsSelected(sessionId));


			String fresnelGroup = msg.m_query.getFirstParamValue("group");
            
            //if (msg.m_query.getRestrictions().size() > 0 || getGraphsSelected().size()>0) {
			
			Iterator<StructuredSchema> i = getSchemas(msg, sessionId).iterator();
			//if(msg.m_query.getRestrictions().size()==0){
				String bucketerParameter="";
				while(i.hasNext()){
					StructuredSchema schema = i.next();
					
					if(schema.countObjects()>0){
						bucketerParameter += "r"+schema.getURI()+",";
					}
				}
				msg.m_query.addRestriction("edu.mit.simile.longwell.query.project.SchemaBasedProjector", "",  "edu.mit.simile.longwell.query.bucket.DistinctValueBucketer", bucketerParameter);
			//}

			
			if (msg.m_query.getRestrictions().size() > 0) {
				
				IAnswer answer = performQuery(msg, vcContext);

				fillContextWithAnswer(msg, vcContext, answer);

	            if ("map".equals(msg.m_query.getFirstParamValue("resultsView"))) {
	                vcContext.put("mapgooglekey",LongwellServer.getMapKey());
	            	vcContext.put("resultsView", "map-results-pane");
	            	msg.getQuery().setParameter("format","GMap");
	            	//exportGMap(msg, answer.getObjects());
	            	
	            	
	            } else if ("calendar".equals(msg.m_query.getFirstParamValue("resultsView"))) {
	                vcContext.put("resultsView", "calendar-results-pane");
	            } else if ("timeline".equals(msg.m_query.getFirstParamValue("resultsView"))) {
	                vcContext.put("resultsView", "timeline-results-pane");
	            } else if ("graph".equals(msg.m_query.getFirstParamValue("resultsView"))) {
	                vcContext.put("resultsView", "graph-results-pane");
	            } else {
	                vcContext.put("resultsView", "results-pane");
	            }
				msg.m_ve.mergeTemplate(
					"templates/commands/browse.vt",
					vcContext,
					msg.m_response.getWriter());
				session.setAttribute("mapMsg", msg);
			} 
			else {
				msg.m_response.sendRedirect(
					new FlairURL(msg).changeCommandQuery("start", "").toURLString());
			}
		} catch (Throwable e) {
			s_logger.error(e);
			e.printStackTrace();
		}

	}

	
	protected Set getSchemas(FlairMessage msg, String id) {
		SortedSet sSchemas = new TreeSet(new Comparator() {
			public boolean equals(Object obj) {
				return false;
			}

			public int compare(Object o1, Object o2) {
				IStructuredSchema ss1 = (IStructuredSchema) o1;
				IStructuredSchema ss2 = (IStructuredSchema) o2;

				return ss1.getURI().compareTo(ss2.getURI());
			}
		});

		sSchemas.addAll(msg.getProfile(id).getSchemaModel().getStructuredSchemas());
		return sSchemas;
	}	
	
	protected IAnswer performQuery(FlairMessage msg, VelocityContext vcContext) throws QueryException {
		Query query = msg.m_query;
		HttpSession session = msg.m_request.getSession();
		String sessionId = session.getId();
		IQueryModel queryModel = (IQueryModel)
			msg.getProfile(sessionId).getStructuredModel(IQueryModel.class);

		IAnswer answer = queryModel.query(query, false);
		
		return answer;
	}

	protected void fillContextWithAnswer(FlairMessage msg, VelocityContext vcContext, IAnswer answer) throws QueryException {
		vcContext.put("answer", answer);

		int pageIndex = 0;
		String page = msg.m_query.getFirstParamValue("page");
		if (page != null) {
			try {
				pageIndex = Integer.parseInt(page);
				pageIndex = Math.max(0, Math.min(pageIndex, answer.getPages().size() - 1));
			} catch (NumberFormatException e) {
				s_logger.warn("Bad page number " + page, e);
			}
		}
		vcContext.put("pageIndex", new Integer(pageIndex));

		vcContext.put("commonProperties", getCommonProperties(answer.getObjects(), msg));
	}

	protected SortedSet getCommonProperties(Set objects, FlairMessage msg) {
		//Profile profile = msg.getProfile();
		HttpSession session = msg.m_request.getSession();
		String sessionId = session.getId();	
		Profile profile = LongwellServer.getServer().getProfile(sessionId);
		Iterator iter = profile.getModel().listSubjects();
		
		Model model = profile.getModel();
		
		SortedSet properties = new TreeSet(new Comparator() {
			String m_locale;

			public boolean equals(Object obj) {
				return false;
			}

			public int compare(Object o1, Object o2) {
				IStructuredProperty p1 = (IStructuredProperty) o1;
				IStructuredProperty p2 = (IStructuredProperty) o2;

				int i = p1.getLabel(m_locale).compareToIgnoreCase(p2.getLabel(m_locale));
				if (i == 0) {
					i = p1.getURI().compareTo(p2.getURI());
				}
				return i;
			}

			public Comparator init(String locale) {
				m_locale = locale;
				return this;
			}
		}.init(msg.m_locale));
		if(objects!=null && objects.size()>0){
			properties.addAll(
					profile.getSchemaModel().getSortedStructuredProperties(objects));
		}
			

		// Default generic properties
		
		{
			properties.add(
				profile.getSchemaModel().getStructuredProperty(
					profile.getModel().createProperty(ITagModel.s_simile_tag)));
		}
		

		return properties;
	}
	
	protected Set getGraphs(FlairMessage msg,String[] listaComuni) throws IOException {
		sGraphs = new TreeSet();
		m_graphsByDataset = SelectGraphsCommand.getGraphsByDataset(msg.m_request.getSession().getId());
		if(m_graphsByDataset!=null && m_graphsByDataset.size()>0){
			for (int i = 0; i < listaComuni.length; i++) {
				File file = new File(listaComuni[i]+".ttl");
				String assemblerFile = file.getName() ;    
				s_logger.info("Loading assembler : "+LongwellServlet.getAssemblerDirectory()+"/"+assemblerFile);
				Dataset ds = TDBFactory.assembleDataset(LongwellServlet.getAssemblerDirectory()+"/"+assemblerFile);		
				Iterator<String> graphNames = ds.listNames();
				while (graphNames.hasNext()) {
				    String graphName = graphNames.next();
				    //System.out.println(graphName);
				    m_graphsByDataset.put(graphName, ds);
				    sGraphs.add(graphName);
				}			
			}
			return sGraphs;
		}
		else{
			msg.m_response.sendRedirect(
					new FlairURL(msg).changeCommandQuery("home", "").toURLString());
			return null;
		}

	}

	
	
	protected HashMap<String, HashMap<String,String>> getGraphsFromDefaultModel(FlairMessage msg,String[] listaComuni) {
		SortedSet sGraphs = new TreeSet();
		HashMap<String, HashMap<String, String>> graphsList = new HashMap<String, HashMap<String,String>>();
		
		m_graphsByDataset = new HashMap<String, Dataset>();
		Model defaultModel=null;
		m_graphsByDataset = SelectGraphsCommand.getGraphsByDataset(msg.m_request.getSession().getId());
		if(m_graphsByDataset!=null && m_graphsByDataset.size()>0){
			for (int i = 0; i < listaComuni.length; i++) {
				File file = new File(listaComuni[i]+"-assembler.ttl");
				String assemblerFile = file.getName() ;    
				s_logger.info("Loading assembler : "+LongwellServlet.getAssemblerDirectory()+"/"+assemblerFile);
				Dataset ds = TDBFactory.assembleDataset(LongwellServlet.getAssemblerDirectory()+"/"+assemblerFile);	
				//defaultModel = ds.getDefaultModel();
				defaultModel = ds.getNamedModel("http://www.provincia.tn.it/bcc/resource/"+listaComuni[i]);
				
	
				Iterator<Statement> statements = defaultModel.listStatements(null, DC.title,(RDFNode) null);
				//for each flow (anagrafe, catasto, titolarita)
				while(statements.hasNext()){
					HashMap<String, String> graphsWithLabel = new HashMap<String, String>();
					Statement statement = statements.next();
					String titleFlow = statement.getObject().toString();
					
					Resource uriFlow = statement.getSubject();
					Property hasPart = new PropertyImpl("http://purl.org/dc/elements/1.1/hasPart");
					Iterator<Statement> graphs = defaultModel.listStatements(uriFlow,hasPart,(RDFNode)null);
					//for each graph
					while(graphs.hasNext()){
						Statement graph = graphs.next();
						Property issued = new PropertyImpl("http://purl.org/dc/elements/1.1/issued");
						Resource graphName = (Resource) graph.getObject();
						Iterator<Statement> graphLabel = defaultModel.listStatements(graphName,issued,(RDFNode)null);
						//read label
						while(graphLabel.hasNext()){
							String label = graphLabel.next().getObject().toString();
							graphsWithLabel.put(graphName.toString(), label);
						}
					}
					graphsList.put(titleFlow, graphsWithLabel);
				}
				
			}
			return graphsList;
		}
		else{
			try {
				msg.m_response.sendRedirect(
						new FlairURL(msg).changeCommandQuery("home", "").toURLString());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		}
		
	}

	
	
	
	
	protected HashMap<String, String> getGraphsSelected(String sessionId){
		HashMap<String, String> hash = new HashMap<String, String>();
		
		if(ChangeModelCommand.getGraphsSelected("graphsSelected"+sessionId)!=null){
			for (int i=0;i<ChangeModelCommand.getGraphsSelected("graphsSelected"+sessionId).length;i++){
				hash.put(ChangeModelCommand.getGraphsSelected("graphsSelected"+sessionId)[i], ChangeModelCommand.getGraphsSelected("graphsSelected"+sessionId)[i]);
			}
			return hash;
		}
		else return null;
	}
	
	   protected void exportGMap(FlairMessage msg, Set objects) throws ServletException {
	    	
	    	VelocityContext vcContext = createContext(msg);
	    	try {
	    		
	    		//String[] properties = StringUtils.splitPreserveAllTokens(msg.m_query.getFirstParamValue("latlong"), ';');
	    		String property = msg.m_query.getFirstParamValue("resultsViewParam");
	    		/*
	    		Set<String> propertyURIs = new HashSet<String>();
	            if(propertyURIs!=null){
	            	for (int i = 0; i < properties.length; i++) {
	                    String property = properties[i];
	                    if (property != null && property.length() > 0) {
	                        propertyURIs.add(property);
	                    }
	                }
	            }
	            */
	            
	            List<MapLocation> locations = new ArrayList<MapLocation>();
	            
	            Profile profile = msg.getProfileFromSession();
	            try {
	                
	               // SchemaModel schemaModel = profile.getSchemaModel().;

	                double minLatitude = Double.POSITIVE_INFINITY;
	                double maxLatitude = Double.NEGATIVE_INFINITY;
	                double minLongitude = Double.POSITIVE_INFINITY;
	                double maxLongitude = Double.NEGATIVE_INFINITY;

	                Map<String,LLRecord> llToRecord = new HashMap<String,LLRecord>();
	                Map<String,Integer> typeToCode = new HashMap<String,Integer>();
	                int codeMax = 0;

	                Iterator i = objects.iterator();

	                while (i.hasNext()) {
	        			
	                	Resource 	res = (Resource) i.next();
	        			
	                    
	                    //URI typeURI = schemaModel.getLearnedClassURIOfItem(object);

	                    String typeURI = res.getProperty(RDF.type).getObject().toString();
	                    
	                    Integer code = typeToCode.get(typeURI);
	                    
	                    if (code == null) {
	                        code = new Integer(codeMax++);
	                        typeToCode.put(typeURI, code);
	                    }

//	                    Iterator j = propertyURIs.iterator();
//	                    while (j.hasNext()) {
//	                        URI property = (URI) j.next();
	                    
	                      Property prop =new PropertyImpl(property) ;
	                      Statement stmt = res.getProperty(prop);
	                      if(stmt!=null){
	                    	  Object result = stmt.getObject();
	                    	  String latlong = result.toString();
	                    	  if (latlong != null) {
	                            LLRecord ll = (LLRecord) llToRecord.get(latlong);
	                            if (ll == null) {
	                                ll = new LLRecord();
	                                int comma = latlong.indexOf(',');
	                                if (comma > 0) {
	                                    try {
	                                        double latitude = Double.parseDouble(latlong.substring(0, comma));
	                                        double longitude = Double.parseDouble(latlong.substring(comma + 1));
	                                        
	                                        if (Double.isNaN(latitude) || Double.isNaN(longitude)) {
	                                            break;
	                                        }
	                                        
	                                        ll.m_objects = new HashSet<String>();
	                                        ll.m_codes = new HashSet<Integer>();

	                                        minLatitude = Math.min(minLatitude, latitude);
	                                        maxLatitude = Math.max(maxLatitude, latitude);
	                                        minLongitude = Math.min(minLongitude, longitude);
	                                        maxLongitude = Math.max(maxLongitude, longitude);

	                                        ll.m_latitude = latitude;
	                                        ll.m_longitude = longitude;
	                                        ll.m_codes.add(code);
	                                    } catch (Exception e) {
	                                        // ignore
	                                    }
	                                }

	                                llToRecord.put(latlong, ll);
	                            }
	                            if (ll.m_objects != null) {
	                                ll.m_objects.add(res.getURI());
	                            }
	                            break;
	                        }
	                    }
	                }

	                double centerLatitude = (minLatitude + maxLatitude) / 2;
	                double centerLongitude = (minLongitude + maxLongitude) / 2;
	                double spanLatitude = Math.max(0.1, (maxLatitude - minLatitude));
	                double spanLongitude = Math.max(0.1, (maxLongitude - minLongitude));

	                msg.m_response.setContentType("text/xml");
	                msg.m_response.setCharacterEncoding("UTF-8");

	                vcContext.put("exportType", "GMap");
	                vcContext.put("centerLatitude", Double.toString(centerLatitude));
	                vcContext.put("centerLongitude", Double.toString(centerLongitude));
	                
	                vcContext.put("spanLatitude", Double.toString(spanLatitude));
	                vcContext.put("spanLongitude", Double.toString(spanLongitude));

	                //LongwellURL url = new LongwellURL(msg);

	                /*
	                 * Legend
	                 */
	               
	                List<LegendEntry> legend = new ArrayList<LegendEntry>();
	                i = typeToCode.keySet().iterator();
	                
	                while (i.hasNext()) {
	                
	                	
	                    String typeURI = i.next().toString();
	                    Integer code = (Integer) typeToCode.get(typeURI);
	                    String markerURL =  msg.m_request.getContextPath() + "/resources/marker?x=0&y=0&s=1&w=40&h=34&label=%20&colorCode=" + code.intValue();
	                    String typeLabel ="test";// schemaModel.getLabel(typeURI, "");
	                    legend.add(new LegendEntry(StringEscapeUtils.escapeXml(markerURL), typeLabel));
	                    
	                }
	                
	                vcContext.put("legend", legend);
					
	                /*
	                 * Individual locations
	                */ 
	                
	                Property rdfType = RDF.type;
	                i = llToRecord.keySet().iterator();
	                int index = 0;
	                while (i.hasNext()) {
	                    String latlong = (String) i.next();
	                    LLRecord ll = (LLRecord) llToRecord.get(latlong);
	                    if (ll.m_objects != null) {
	                        int objectCount = ll.m_objects.size();
	                        String markerLabel = objectCount > 1 ? Integer.toString(objectCount) : "+";

	                        String colorCode = "";
	                        Iterator j = ll.m_codes.iterator();
	                        while (j.hasNext()) {
	                            Integer integer = (Integer) j.next();
	                            if (colorCode.length() > 0) {
	                                colorCode += ",";
	                            }
	                            colorCode += integer;
	                        }

	                        String markerURL = "http://localhost:9080/Longwell2/resources/marker?x=0&y=0&s=1&w=40&h=34" + "&label=" + markerLabel + "&colorCode=" + colorCode;

	                        MapLocation location = new MapLocation(index++, ll.m_latitude, ll.m_longitude, StringEscapeUtils.escapeXml(markerURL));

	                        Iterator n = ll.m_objects.iterator();
	                        
	                        while (n.hasNext()) {
	                            Object object = (Object) n.next();
	                            Resource reso = profile.getModel().createResource(object.toString());
	                            //Resource reso = (Resource) object;
	                            String objectURI = object.toString();
	                            String label = StringEscapeUtils.escapeXml("test");//schemaModel.getLabel(object, ""));
	                            String objectURL = StringEscapeUtils.escapeXml(msg.getURL(reso));
	/*
	                            StringBuffer editURL = new StringBuffer();
	                            {
	                                editURL.append(url.getContextPath() + "/resources/forms/?uri="
	                                        + Utilities.encode(objectURI));

	                                Iterator types = profile.getQueryManager().listObjectsOfProperty(c, object, rdfType).iterator();
	                                while (types.hasNext()) {
	                                    URI typeURI = (URI) types.next();
	                                    editURL.append("&amp;type=" + Utilities.encode(typeURI.toString()));
	                                }
	                            }

	                            LongwellURL removeURL = url.changeCommandQuery("remove-location", "");
	                            {
	                                removeURL.getQuery().setParameter("objectURI", objectURI);
	                                removeURL.getQuery().setParameter("latlong", msg.m_query.getFirstParamValue("latlong"));
	                            }
	                            */
	                            MapLocationItem item = new MapLocationItem(label, objectURL,"","");// editURL.toString(), StringEscapeUtils.escapeXml(removeURL.toURLString()));
	                            location.addItem(item);
	                            
	                        }
	                        locations.add(location);
	                        
	                    }
	            
	                }
	            } finally {
	               // if (c != null) c.close();
	            }
	            
	            vcContext.put("locations", locations);
	            
	           // msg.m_ve.mergeTemplate("templates/panes/map-results-pane.vt", vcContext, msg.m_response.getWriter());
	            msg.m_ve.mergeTemplate("templates/export/gmap-results.vt", vcContext, msg.m_response.getWriter());
	          //  msg.m_ve.mergeTemplate("templates/commands/export.vt", vcContext, msg.m_response.getWriter());
	        } catch (Exception e) {
	            throw new ServletException(e);
	        }
	    
	    }
	    public class MapLocation {
			private int m_index;
	    		private double m_latitude;
	    		private double m_longitude;
	    		private String m_markerURL;
	    		private List<MapLocationItem> m_items;
	    		
	    		public MapLocation(int index, double latitude, double longitude, String marker) {
	    			m_items = new ArrayList<MapLocationItem>();
	    			m_index = index;
	    			m_latitude = latitude;
	    			m_longitude = longitude;
	    			m_markerURL = marker;
	    		}
	    		
	    		public void addItem(MapLocationItem item) {
	    			m_items.add(item);
	    		}
	    		
	    		public double lat() {
	    			return m_latitude;
	    		}
	    		
	    		public String getLatitude() {
	    			return Double.toString(m_latitude);
	    		}
	    		
	    		public double lng() {
	    			return m_longitude;
	    		}
	    		
	    		public String getLongitude() {
	    			return Double.toString(m_longitude);
	    		}
	    		
	    		public int getIndex() {
	    			return m_index;
	    		}
	    		
	    		public String getMarkerURL() {
	    			return m_markerURL;
	    		}
	    		
	    		public List getItems() {
	    			return m_items;
	    		}
	    		
	    		public Iterator getItemsIterator() {
	    			return m_items.iterator();
	    		}
	    }
	    
	    public class MapLocationItem {
			private String m_label;
			private String m_itemURL;
			private String m_editURL;
			private String m_removeURL;

			public MapLocationItem(String label, String URL, String editURL, String removeURL) {
				m_label = label;
				m_itemURL = URL;
				m_editURL = editURL;
				m_removeURL = removeURL;
			}
			
			public String getLabel() {
				return m_label;
			}
			
			public String getItemURL() {
				return m_itemURL;
			}
			
			public String getEditURL() {
				return m_editURL;
			}
			
			public String getRemoveURL() {
				return m_removeURL;
			}
	    }
	    
	    static private class LLRecord {
	        double m_latitude;

	        double m_longitude;

	        Set<String> m_objects;

	        Set<Integer> m_codes;
	    }
	    
	    public class LegendEntry {
	    		private String m_markerURL;
	    		
	    		private String m_label;
	    		
	    		public LegendEntry(String marker, String label) {
	    			m_markerURL = marker;
	    			m_label = label;
	    		}
	    		
	    		public String getMarkerURL() {
	    			return m_markerURL;
	    		}
	    		
	    		public String getLabel() {
	    			return m_label;
	    		}
	    }    
	    
		
	    protected void setContentType(String format, HttpServletResponse response) {
	        if ("RDFXML".equals(format)) {
	            response.setContentType("application/rdf+xml");
	        } else if ("map".equals(format)) {
	            response.setContentType("application/xml");
	        } else if ("N3".equals(format)) {
	            response.setContentType("application/n3");
	        } else {
	            response.setContentType("text/plain");
	        }
	    }	   
	
	
}
